package com.airbnb.lottie.parser;

import com.airbnb.lottie.L;
import com.airbnb.lottie.parser.moshi.JsonReader;
import com.airbnb.lottie.LottieComposition;
import com.airbnb.lottie.LottieImageAsset;
import com.airbnb.lottie.model.Font;
import com.airbnb.lottie.model.FontCharacter;
import com.airbnb.lottie.model.Marker;
import com.airbnb.lottie.model.layer.Layer;
import com.airbnb.lottie.utils.HMOSLogUtil;
import com.airbnb.lottie.utils.Utils;

import ohos.agp.utils.Rect;
import ohos.utils.LongPlainArray;
import ohos.utils.PlainArray;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LottieCompositionMoshiParser {
    private static final JsonReader.Options NAMES = JsonReader.Options.of("w", // 0
        "h", // 1
        "ip", // 2
        "op", // 3
        "fr", // 4
        "v", // 5
        "layers", // 6
        "assets", // 7
        "fonts", // 8
        "chars", // 9
        "markers" // 10
    );

    public static LottieComposition parse(JsonReader reader) throws IOException {
        float scale = Utils.dpScale();
        float startFrame = 0f;
        float endFrame = 0f;
        float frameRate = 0f;
        final LongPlainArray<Layer> layerMap = new LongPlainArray<>();
        final List<Layer> layers = new ArrayList<>();
        int width = 0;
        int height = 0;
        Map<String, List<Layer>> precomps = new HashMap<>();
        Map<String, LottieImageAsset> images = new HashMap<>();
        Map<String, Font> fonts = new HashMap<>();
        List<Marker> markers = new ArrayList<>();
        PlainArray<FontCharacter> characters = new PlainArray<>();

        LottieComposition composition = new LottieComposition();
        reader.beginObject();
        while (reader.hasNext()) {
            switch (reader.selectName(NAMES)) {
                case 0:
                    width = reader.nextInt();
                    break;
                case 1:
                    height = reader.nextInt();
                    break;
                case 2:
                    startFrame = (float) reader.nextDouble();
                    break;
                case 3:
                    endFrame = (float) reader.nextDouble() - 0.01f;
                    break;
                case 4:
                    frameRate = (float) reader.nextDouble();
                    break;
                case 5:
                    String version = reader.nextString();
                    String[] versions = version.split("\\.");
                    int majorVersion = Integer.parseInt(versions[0]);
                    int minorVersion = Integer.parseInt(versions[1]);
                    int patchVersion = Integer.parseInt(versions[2]);
                    if (!Utils.isAtLeastVersion(majorVersion, minorVersion, patchVersion, 4, 4, 0)) {
                        composition.addWarning("Lottie only supports bodymovin >= 4.4.0");
                    }
                    break;
                case 6:
                    parseLayers(reader, composition, layers, layerMap);
                    break;
                case 7:
                    parseAssets(reader, composition, precomps, images);
                    break;
                case 8:
                    parseFonts(reader, fonts);
                    break;
                case 9:
                    parseChars(reader, composition, characters);
                    break;
                case 10:
                    parseMarkers(reader, composition, markers);
                    break;
                default:
                    reader.skipName();
                    reader.skipValue();
            }
        }
        int scaledWidth = (int) (width * scale);
        int scaledHeight = (int) (height * scale);
        Rect bounds = new Rect(0, 0, scaledWidth, scaledHeight);

        composition.init(bounds, startFrame, endFrame, frameRate, layers, layerMap, precomps, images, characters,
            fonts, markers);
        /*composition.init(bounds, 31, 391, 1000, layers, layerMap, precomps, images, characters,
                fonts, markers);*/

        return composition;
    }

    private static void parseLayers(JsonReader reader, LottieComposition composition, List<Layer> layers,
        LongPlainArray<Layer> layerMap) throws IOException {
        int imageCount = 0;
        reader.beginArray();
        while (reader.hasNext()) {
            Layer layer = LayerParser.parse(reader, composition);
            if (layer.getLayerType() == Layer.LayerType.IMAGE) {
                imageCount++;
            }
            layers.add(layer);
            layerMap.put(layer.getId(), layer);

            if (imageCount > 4) {
                HMOSLogUtil.warn(L.TAG, "You have " + imageCount + " images. Lottie should primarily be "
                    + "used with shapes. If you are using Adobe Illustrator, convert the Illustrator layers"
                    + " to shape layers.");
            }
        }
        reader.endArray();
    }

    static JsonReader.Options ASSETS_NAMES = JsonReader.Options.of("id", // 0
        "layers", // 1
        "w", // 2
        "h", // 3
        "p", // 4
        "u" // 5
    );

    private static void parseAssets(JsonReader reader, LottieComposition composition, Map<String, List<Layer>> precomps,
        Map<String, LottieImageAsset> images) throws IOException {
        reader.beginArray();
        while (reader.hasNext()) {
            String id = null;
            // For precomps
            List<Layer> layers = new ArrayList<>();
            LongPlainArray<Layer> layerMap = new LongPlainArray<>();
            // For images
            int width = 0;
            int height = 0;
            String imageFileName = null;
            String relativeFolder = null;
            reader.beginObject();
            while (reader.hasNext()) {
                switch (reader.selectName(ASSETS_NAMES)) {
                    case 0:
                        id = reader.nextString();
                        break;
                    case 1:
                        reader.beginArray();
                        while (reader.hasNext()) {
                            Layer layer = LayerParser.parse(reader, composition);
                            layerMap.put(layer.getId(), layer);
                            layers.add(layer);
                        }
                        reader.endArray();
                        break;
                    case 2:
                        width = reader.nextInt();
                        break;
                    case 3:
                        height = reader.nextInt();
                        break;
                    case 4:
                        imageFileName = reader.nextString();
                        break;
                    case 5:
                        relativeFolder = reader.nextString();
                        break;
                    default:
                        reader.skipName();
                        reader.skipValue();
                }
            }
            reader.endObject();
            if (imageFileName != null) {
                LottieImageAsset image = new LottieImageAsset(width, height, id, imageFileName, relativeFolder);
                images.put(image.getId(), image);
            } else {
                precomps.put(id, layers);
            }
        }
        reader.endArray();
    }

    private static final JsonReader.Options FONT_NAMES = JsonReader.Options.of("list");

    private static void parseFonts(JsonReader reader, Map<String, Font> fonts) throws IOException {
        reader.beginObject();
        while (reader.hasNext()) {
            switch (reader.selectName(FONT_NAMES)) {
                case 0:
                    reader.beginArray();
                    while (reader.hasNext()) {
                        Font font = FontParser.parse(reader);
                        fonts.put(font.getName(), font);
                    }
                    reader.endArray();
                    break;
                default:
                    reader.skipName();
                    reader.skipValue();
            }
        }
        reader.endObject();
    }

    private static void parseChars(JsonReader reader, LottieComposition composition,
        PlainArray<FontCharacter> characters) throws IOException {
        reader.beginArray();
        while (reader.hasNext()) {
            FontCharacter character = FontCharacterParser.parse(reader, composition);
            characters.put(character.hashCode(), character);
        }
        reader.endArray();
    }

    private static final JsonReader.Options MARKER_NAMES = JsonReader.Options.of("cm", "tm", "dr");

    private static void parseMarkers(JsonReader reader, LottieComposition composition, List<Marker> markers)
        throws IOException {
        reader.beginArray();
        while (reader.hasNext()) {
            String comment = null;
            float frame = 0f;
            float durationFrames = 0f;
            reader.beginObject();
            while (reader.hasNext()) {
                switch (reader.selectName(MARKER_NAMES)) {
                    case 0:
                        comment = reader.nextString();
                        break;
                    case 1:
                        frame = (float) reader.nextDouble();
                        break;
                    case 2:
                        durationFrames = (float) reader.nextDouble();
                        break;
                    default:
                        reader.skipName();
                        reader.skipValue();
                }
            }
            reader.endObject();
            markers.add(new Marker(comment, frame, durationFrames));
        }
        reader.endArray();
    }
}
